package cn.jingyuan.owl.web.oauth2.error;

import cn.jingyuan.bee.exception.SystemException;
import cn.jingyuan.bee.fastjson.FastJsonUtils;
import cn.jingyuan.swan.cloud.core.ExCode;
import cn.jingyuan.swan.cloud.core.Result;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.*;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.*;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class DefaultOAuth2ExceptionSupport {

    public static void doCommence(HttpServletRequest request, HttpServletResponse response, Exception ex)
        throws IOException, ServletException {
        Result<?> rs = resolve(ex);
        Integer status = rs.getHttpStatus();

        String body = FastJsonUtils.json(rs);

        response.setStatus(status);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response
            .getWriter()
            .write(body);
    }

    public static Result resolve(Exception ex) {
        if (ex instanceof SystemException) {
            SystemException sex = (SystemException) ex;
            int status = HttpStatus.FORBIDDEN.value();

            return new Result()
                .setCode(sex.getCode())
                .setCodeMsg(sex.getCodeMsg())
                .setHttpStatus(status)
                .setMessage(sex.getMessage());
        }

        if (ex instanceof UsernameNotFoundException) {
            return buildResult(ExCode.ACCOUNT_NOT_EXIST, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof BadCredentialsException) {
            return buildResult(ExCode.ACCOUNT_NOT_EXIST, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof AccountExpiredException) {
            return buildResult(ExCode.ACCOUNT_EXPIRED, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof LockedException) {
            return buildResult(ExCode.ACCOUNT_LOCKED, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof DisabledException) {
            return buildResult(ExCode.ACCOUNT_DISABLED, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof CredentialsExpiredException) {
            return buildResult(ExCode.CREDENTIALS_EXPIRED, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof InvalidClientException) {
            return buildResult(ExCode.INVALID_CLIENT, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof UnauthorizedClientException) {
            return buildResult(ExCode.UNAUTHORIZED_CLIENT, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof InsufficientAuthenticationException ||
            ex instanceof AuthenticationCredentialsNotFoundException) {
            return buildResult(ExCode.UNAUTHORIZED, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof InvalidGrantException) {
            ExCode exCode = ExCode.WARNING;
            String exMsg = ex.getMessage();

            if ("Bad credentials".contains(exMsg)) {
                exCode = ExCode.BAD_CREDENTIALS;
            } else if ("User is disabled".contains(exMsg)) {
                exCode = ExCode.ACCOUNT_DISABLED;
            } else if ("User account is locked".contains(exMsg)) {
                exCode = ExCode.ACCOUNT_LOCKED;
            }

            return buildResult(exCode, HttpStatus.INTERNAL_SERVER_ERROR);
        }

        if (ex instanceof InvalidScopeException) {
            return buildResult(ExCode.INVALID_SCOPE, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof InvalidTokenException) {
            return buildResult(ExCode.INVALID_TOKEN, HttpStatus.UNAUTHORIZED);
        }

        if (ex instanceof InvalidRequestException) {
            return buildResult(ExCode.INVALID_REQUEST, HttpStatus.BAD_REQUEST);
        }

        if (ex instanceof UnsupportedGrantTypeException) {
            return buildResult(ExCode.UNSUPPORTED_GRANT_TYPE, HttpStatus.INTERNAL_SERVER_ERROR);
        }

        if (ex instanceof UnsupportedResponseTypeException) {
            return buildResult(ExCode.UNSUPPORTED_RESPONSE_TYPE, HttpStatus.INTERNAL_SERVER_ERROR);
        }

        if (ex instanceof UserDeniedAuthorizationException) {
            return buildResult(ExCode.FORBIDDEN, HttpStatus.FORBIDDEN);
        }

        if (ex instanceof AccessDeniedException) {
            return buildResult(ExCode.FORBIDDEN, HttpStatus.FORBIDDEN);
        }

        return buildResult(ExCode.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    private static <D> Result<D> buildResult(ExCode exCode, HttpStatus status) {
        return new Result<D>()
            .setCode(exCode.getCode())
            .setCodeMsg(exCode.getCodeMsg())
            .setHttpStatus(status.value());

    }

}
